<template>
  <div class="call-container" v-if="dialling || calling || isDialled">
    <div class="choose" v-if="isDialled">
      <div class="title">{{ toAccount }}&nbsp;来电话了</div>
      <div class="buttons">
        <div class="accept" @click="accept"></div>
        <div class="refuse" @click="refuse"></div>
      </div>
    </div>
    <div class="call" v-if="dialling || calling">
      <div class="title" v-if="dialling">正在呼叫&nbsp;{{ toAccount }}...</div>
      <div
        id="local"
        @click="changeMainVideo"
        :class="isLocalMain ? 'big' : 'small'"
        v-show="calling"
      ></div>
      <div
        name="remote"
        :class="isLocalMain ? 'small' : 'big'"
        @click="changeMainVideo"
        v-show="calling"
      ></div>
      <div class="duration" v-show="calling">
        {{ formatDurationStr }}
      </div>
      <div class="buttons">
        <div
          :class="isCamOn ? 'videoOn' : 'videoOff'"
          @click="videoHandler"
        ></div>
        <div class="refuse" @click="leave"></div>
        <div :class="isMicOn ? 'micOn' : 'micOff'" @click="micHandler"></div>
      </div>
      <div
        class="mask"
        v-show="maskShow"
        :class="isLocalMain ? 'big' : 'small'"
        @click="changeMainVideo"
      >
        <div>
          <img class="image" src="../../../assets/image/camera-max.png" />
          <p class="notice">摄像头未打开</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import RtcClient from "@/utils/rtc-client";
import { ACTION, VERSION } from "@/utils/trtcCustomMessageMap";
import { mapGetters, mapState } from "vuex";
import { formatDuration } from "@/utils/formatDuration";

export default {
  name: "CallLayer",
  data() {
    return {
      Trtc: undefined,
      isCamOn: true,
      isMicOn: true,
      maskShow: false,
      isLocalMain: true, // 本地视频是否是主屏幕显示
      start: 0,
      end: 0,
      duration: 0,
      hangUpTimer: 0, // 通话计时id
      ready: false,
      dialling: false, // 是否拨打电话中
      calling: false, // 是否通话中
      isDialled: false // 是否被呼叫
    };
  },
  computed: {
    ...mapGetters(["toAccount", "currentConversationType"]),
    ...mapState({
      userID: state => state.user.userID,
      userSig: state => state.user.userSig,
      videoRoom: state => state.video.videoRoom,
      sdkAppID: state => state.user.sdkAppID
    }),
    formatDurationStr() {
      return formatDuration(this.duration);
    }
  },
  created() {
    window.addEventListener("beforeunload", () => {
      this.videoCallLogOut();
    });
    window.addEventListener("leave", () => {
      this.videoCallLogOut();
    });
  },
  mounted() {
    this.$bus.$on("isCalled", this.isCalled);
    this.$bus.$on("missCall", this.missCall);
    this.$bus.$on("isRefused", this.isRefused);
    this.$bus.$on("isAccept", this.isAccept);
    this.$bus.$on("isHungUp", this.isHungUp);
    this.$bus.$on("busy", this.busy);
    this.$bus.$on("video-call", this.videoCall);
  },
  beforeDestroy() {
    this.$bus.$off("isCalled", this.isCalled);
    this.$bus.$off("missCall", this.missCall);
    this.$bus.$off("isRefused", this.isRefused);
    this.$bus.$off("isAccept", this.isAccept);
    this.$bus.$off("isHungUp", this.isHungUp);
    this.$bus.$off("busy", this.busy);
    this.$bus.$off("video-call", this.videoCall);
  },
  methods: {
    videoCallLogOut() {
      // 针对，刷新页面，关闭Tab，登出情况下，通话断开的逻辑
      if (this.dialling || this.calling) {
        this.leave();
      }
      if (this.isDialled) {
        this.refuse();
      }
    },
    changeState(state, boolean) {
      let stateList = ["dialling", "isDialled", "calling"];
      stateList.forEach(item => {
        this[item] = item === state ? boolean : false;
      });
      this.$store.commit(
        "UPDATE_ISBUSY",
        stateList.some(item => this[item])
      ); // 若stateList 中存在 true , isBusy 为 true
    },
    async initTrtc(options) {
      // 初始化 trtc 进入房间
      this.Trtc = new RtcClient(options);
      await this.Trtc.createLocalStream({ audio: true, video: true })
        .then(() => {
          // 在进房之前，判断设备
          this.Trtc.join();
          this.ready = true;
          this.isCamOn = true;
          this.maskShow = false;
        })
        .catch(() => {
          alert("请确认已连接摄像头和麦克风并授予其访问权限！");
          this.ready = false;
        });
    },
    videoCall() {
      // 发起通话
      if (this.calling) {
        // 避免通话按钮多次快速的点击
        return;
      }
      this.isLocalMain = true;
      this.$store.commit("GENERATE_VIDEO_ROOM"); // 初始化房间号
      const options = {
        userId: this.userID,
        userSig: this.userSig,
        roomId: this.videoRoom,
        sdkAppId: this.sdkAppID
      };
      this.initTrtc(options).then(() => {
        if (!this.ready) return;
        this.changeState("dialling", true);
        this.timer = setTimeout(
          this.timeout,
          process.env.NODE_ENV === "development" ? 999999 : 60000
        ); // 开始计时器，开发环境超时时间较长，便于调试
        this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_DIALING);
      });
    },
    leave() {
      // 离开房间，发起方挂断
      if (!this.calling) {
        // 还没有通话，单方面挂断
        this.Trtc.leave();
        clearTimeout(this.timer);
        this.changeState("dialling", false);
        this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_SPONSOR_CANCEL);
        return;
      }
      this.hangUp(); // 通话一段时间之后，某一方面结束通话
    },
    timeout() {
      // 通话超时
      this.changeState("dialling", false);
      this.Trtc.leave();
      this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_SPONSOR_TIMEOUT);
    },
    isCalled() {
      // 被呼叫
      this.changeState("isDialled", true);
    },
    missCall() {
      // 错过电话，也就是发起方的电话超时挂断或自己挂断
      this.changeState("isDialled", false);
    },
    refuse() {
      // 拒绝电话
      this.changeState("isDialled", false);
      this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_REJECT);
    },
    isRefused() {
      // 对方拒绝通话
      this.changeState("dialling", false);
      clearTimeout(this.timer);
    },
    resetDuration(duration) {
      this.duration = duration;
      this.hangUpTimer = setTimeout(() => {
        let now = new Date();
        this.resetDuration(parseInt((now - this.start) / 1000));
      }, 1000);
    },
    accept() {
      // 接听电话
      this.changeState("calling", true);
      const options = {
        userId: this.userID,
        userSig: this.userSig,
        roomId: this.videoRoom,
        sdkAppId: this.sdkAppID
      };
      this.initTrtc(options).then(() => {
        if (!this.ready) {
          this.changeState("calling", false);
          this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_ERROR);
          return;
        }
        this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_ACCEPTED);
        this.start = new Date();
        clearTimeout(this.hangUpTimer);
        this.resetDuration(0);
      });
    },
    isAccept() {
      // 对方接听自己发起的电话
      clearTimeout(this.timer);
      this.changeState("calling", true);
      clearTimeout(this.hangUpTimer);
      this.resetDuration(0);
      this.start = new Date();
    },
    hangUp() {
      // 通话一段时间之后，某一方挂断电话
      this.changeState("calling", false);
      this.Trtc.leave();
      this.end = new Date();
      const duration = parseInt((this.end - this.start) / 1000);
      this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_HANGUP, duration);
      clearTimeout(this.hangUpTimer);
    },
    isHungUp() {
      // 通话一段时间之后，对方挂断电话
      if (this.calling) {
        this.changeState("calling", false);
        this.Trtc.leave();
        clearTimeout(this.hangUpTimer);
      }
    },
    busy(videoPayload, messageItem) {
      videoPayload.action = ACTION.VIDEO_CALL_ACTION_LINE_BUSY;
      const message = this.tim.createCustomMessage({
        to: messageItem.from,
        conversationType: this.currentConversationType,
        payload: {
          data: JSON.stringify(videoPayload),
          description: "",
          extension: ""
        }
      });
      this.$store.commit("pushCurrentMessageList", message);
      this.tim.sendMessage(message);
    },
    videoHandler() {
      // 是否打开摄像头
      if (this.isCamOn) {
        this.isCamOn = false;
        this.maskShow = true;
        this.Trtc.muteLocalVideo();
      } else {
        this.isCamOn = true;
        this.maskShow = false;
        this.Trtc.unmuteLocalVideo();
      }
    },
    micHandler() {
      // 是否打开麦克风
      if (this.isMicOn) {
        this.isMicOn = false;
        this.Trtc.muteLocalAudio();
      } else {
        this.isMicOn = true;
        this.Trtc.unmuteLocalAudio();
      }
    },
    sendVideoMessage(action, duration = 0) {
      const options = {
        room_id: this.videoRoom,
        call_id: "",
        action,
        version: VERSION,
        invited_list: [],
        duration
      };
      const message = this.tim.createCustomMessage({
        to: this.toAccount,
        conversationType: this.currentConversationType,
        payload: {
          data: JSON.stringify(options),
          description: "",
          extension: ""
        }
      });
      this.$store.commit("pushCurrentMessageList", message);
      this.tim.sendMessage(message);
    },
    changeMainVideo() {
      if (!this.calling) {
        return;
      }
      this.isLocalMain = !this.isLocalMain;
    }
  }
};
</script>

<style lang="stylus" scoped>
.call-container
  background center url("")
  background-size cover
  position absolute
  z-index 999
.accept, .refuse, .videoOn, .videoOff, .micOn, .micOff
  height 50px
  width 50px
  box-sizing border-box
  border-radius 50%
  cursor:pointer
.accept
  background center no-repeat url("../../../assets/image/call.png")
  background-size 60%
  background-color $success
.refuse
  background center no-repeat url("../../../assets/image/close.png")
  background-size 70%
  background-color $danger
.videoOn
  background center no-repeat url("../../../assets/image/big-camera-on.png")
.videoOff
  background center no-repeat url("../../../assets/image/big-camera-off.png")
.micOn
  background center no-repeat url("../../../assets/image/big-mic-on.png")
.micOff
  background center no-repeat url("../../../assets/image/big-mic-off.png")
.buttons
  position absolute
  z-index 20
  width 100%
  top 75%
  display flex
  justify-content space-around
.duration
  color #fff
  position absolute
  z-index 20
  width 100%
  top 70%
  display flex
  justify-content center
.mask
  position absolute
  z-index 10
  background #D8D8D8
  height 100%
  width 100%
  display flex
  align-items center
  justify-content center
  space
  .image
    margin-left 15%
  .notice
    color #888888
.choose, .call
  color #fff
  background-color rgba(0, 0, 0, 0.8)
  height 100%
  width 100%
.title
  margin 25% 0 0 0
  text-align center
  width 100%
  position absolute
  z-index 10
  color  #fff
  font-size 40px
  font-weight 700
.big
  position absolute
  height 100%
  width 100%
.small
  position absolute
  z-index 999
  border-style solid
  border-width 1px
  border-color #808080
  height 50%
  width 50%
</style>
